package roborally;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Model;

/**
 * @version	3.0
 * @author 	Lies Tambeur 	r0264469	2Bcwsbbh
 * @author	Eline Vanrykel	r0264543	2Bcwselt 
 */
public class EnergyAmount {
	
	private double amount;
	private EnergyUnit unit;
	
	public EnergyAmount(double amount, EnergyUnit unit) {
		setAmount(amount);
		setUnit(unit);
	}
	
	public EnergyAmount(){
		this(0,EnergyUnit.WATTSECONDS);
	}

	private void setUnit(EnergyUnit unit) {
		assert isLegalUnit(unit);
		this.unit = unit;		
	}
	
	public static boolean isLegalUnit(EnergyUnit unit){
		return unit != null;
	}
	
	public static boolean isLegalAmount(double amount){
		return true;
	}

	private void setAmount(double amount) {
		assert(isLegalAmount(amount)); 
		this.amount = amount;
	}
	
	@Basic
	public EnergyUnit getUnit(){
		return this.unit;
	}
	
	@Basic
	public double getAmount(){
		return this.amount;
	}
	
	public EnergyAmount toEnergyUnit(EnergyUnit unit)
			throws IllegalArgumentException {
		if (!isLegalUnit(unit)){
			throw new IllegalArgumentException("Illegal energy unit!");
		}
		double conversion = getUnit().toEnergyUnit(unit);
		double amount = getAmount();
		amount = amount*conversion;
		return new EnergyAmount(amount, unit);
	}
	
	/**
	 * Calculates the sum of this energyAmount and other.
	 * @param 	other
	 * 			The amount of energy to be added to the current amount of energy.
	 * @return	Returns a new EnergyAmount object that is the sum of this energyAmount and other.
	 */
	@Model
	public EnergyAmount addAndReturn(EnergyAmount other){
		if (other == null){
			throw new IllegalArgumentException();
		}
		else{
			return new EnergyAmount(this.getAmount()+other.toEnergyUnit(this.getUnit()).getAmount(),this.getUnit());
		}
	}

	/**
	 * Calculates the difference of this energyAmount and other.
	 * @param 	other
	 * 			The amount of energy to be subtracted to the current amount of energy.
	 * @return	Returns a new EnergyAmount object that is the difference of this energyAmount and other.
	 */
	@Model
	public EnergyAmount subtractAndReturn(EnergyAmount other){
		if (other == null){
			throw new IllegalArgumentException();
		}
		else{
			return new EnergyAmount(this.getAmount()-other.toEnergyUnit(this.getUnit()).getAmount(),this.getUnit());
		}
	}
	
	@Override
	public String toString(){
		return this.getAmount()+" "+this.getUnit().getAbbreviation();
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		long temp;
		temp = Double.doubleToLongBits(amount);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		result = prime * result + ((unit == null) ? 0 : unit.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		else{
			return this.getAmount() == ((EnergyAmount)obj).toEnergyUnit(this.getUnit()).getAmount();
		}
	}

	public EnergyAmount getCopy() {
		return new EnergyAmount(this.getAmount(), this.getUnit());
	}
	
	/**
	 * Returns whether this energyAmount is smaller than other.
	 * @param 	other
	 * 			The amount of energy to be compared against.
	 * @return	True only if this energyAmount is strictly smaller than other.
	 */
	@Model
	public boolean isSmallerThan(EnergyAmount other){
		EnergyAmount theOther = other.toEnergyUnit(this.getUnit());
		return this.getAmount() < theOther.getAmount();
	}
	
	/**
	 * Returns whether this energyAmount is smaller than or equal to other.
	 * @param 	other
	 * 			The amount of energy to be compared against.
	 * @return	True only if this energyAmount is smaller than or equal to other.
	 */
	@Model
	public boolean isSmallerThanOrEqualTo(EnergyAmount other){
		EnergyAmount theOther = other.toEnergyUnit(this.getUnit());
		return this.getAmount() <= theOther.getAmount();
	}
	
	/**
	 * Calculates the quotient of this energyAmount and denominator.
	 * @param 	denominator
	 * 			The number the amount should be divided with.
	 * @return	Returns a new EnergyAmount object that is the quotient of this energyAmount and denominator.
	 */
	@Model
	public EnergyAmount divideAndReturn(int denominator){
		assert denominator > 0;
		EnergyAmount result = new EnergyAmount(this.getAmount()/denominator,this.getUnit());
		return result;
	}
	
	/**
	 * Calculates the product of this energyAmount and factor.
	 * @param 	factor
	 * 			The number the amount should be multiplied with.
	 * @return	Returns a new EnergyAmount object that is the product of this energyAmount and other.
	 */
	@Model
	public EnergyAmount multiplyAndReturn(double factor){
		assert factor > 0;
		EnergyAmount result = new EnergyAmount(this.getAmount()*factor,this.getUnit());
		return result;
	}
	
}
